package main

import (
	"ac-julink/tool/sha256"
	"bufio"
	"crypto/tls"
	"errors"
	"flag"
	"fmt"
	"math/rand"
	"net"
	"strings"
	"sync"
	"time"
)

var paramString_101 string = "{\"id\":101,\"wid\":\"wifiid\",\"ver\":\"9.9.9\"}"

var paramString_901 string = "{\"id\":901,\"token\":\"tokenv\",\"status\":[{\"msgsubtype\":0,\"msgtype\":30,\"result\":\"hAU=\"},{\"msgsubtype\":0,\"msgtype\":102,\"result\":\"9PUBQEkBAP4BAQEBAGYAAQEAIBsdHICAgAAAAAAAAQEBAQIAAAAAAAAAAAAdGSUl+gDkgACAAAAAAAE/AAAAAAAAAAAAAAAAAAAAAIAACA30+w==\"}],\"err\":0}"

var paramString_102 string = "{\"id\":102,\"token\":\"tokenv\"}"

var paramString_103 string = "{\"id\":103,\"token\":\"tokenv\"}"

func main() {
	featureCode := "865001100010001003000000"
	encryptKey := "d237cd84fc9419a77199a1fc64f0ce8a"
	serverAddr := "test-link.hijuconn.com:443"
	loopNum := 1
	reqCount := 1000
	maxSleepInt := 1
	heartbeat := 30
	startNum := 0
	sleepInterval := 10
	var mode string

	flag.StringVar(&mode, "m", "0", "0:auto;1:manual")
	flag.StringVar(&featureCode, "f", "865001100010001003000000", "featureCode")
	flag.StringVar(&encryptKey, "e", "d237cd84fc9419a77199a1fc64f0ce8a", "encryptKey")
	flag.StringVar(&serverAddr, "s", "test-link.hijuconn.com:443", "serverAddr")
	flag.IntVar(&loopNum, "n", 10, "terminal num")
	flag.IntVar(&reqCount, "c", 1, "req num")
	flag.IntVar(&maxSleepInt, "t", 30, "sleep time")
	flag.IntVar(&heartbeat, "hb", 1*60, "heatbeat")
	flag.IntVar(&startNum, "pos", 100000000000, "start position")
	flag.IntVar(&sleepInterval, "sleepInterval", 1000, "sleep interval to connect the next one")

	flag.Parse()

	fmt.Println(fmt.Sprintf("sleepInterval: %d", sleepInterval))

	fmt.Println("***************start********************")
	var waitG *sync.WaitGroup = &sync.WaitGroup{}
	idPos := 0
	begin := fmt.Sprintf("%v", time.Now().Format(time.RFC3339Nano))
	for i := 1; i <= loopNum; i++ {
		idPos = startNum + i
		wifiId := fmt.Sprintf("%s%12d", featureCode, idPos)
		waitG.Add(i)
		fmt.Println(fmt.Sprintf("start terminal:%d", i))
		go terminal(serverAddr, i, reqCount, maxSleepInt, heartbeat, wifiId, encryptKey, waitG)
		time.Sleep(time.Duration(sleepInterval*1000) * time.Nanosecond)
	}
	println(fmt.Sprintf("begin at: %v, finish at: %v", begin, time.Now().Format(time.RFC3339)))
	waitG.Wait()
	fmt.Println("***************shutdown********************")
}

func terminal(serverAddr string, num, count, maxSleep, heartbeatSleep int, wifiId, encryptKey string, waitG *sync.WaitGroup) {
	defer func() {
		if pErr := recover(); pErr != nil {
			fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have panic err:%+v", time.Now(), num, wifiId, pErr))
		}
	}()

	defer func() {
		waitG.Done()
	}()
	//var sendChan chan string = make(chan string, 64)
	errConnectCount := 0
	connectCount := 0
	var sendChan chan string = make(chan string, 64)
	for {
		var connTls *tls.Conn
		var connTcp net.Conn
		var err error
		if strings.Contains(serverAddr, ":443") {
			conf := &tls.Config{
				InsecureSkipVerify: true,
			}
			connTls, err = tls.Dial("tcp", serverAddr, conf)
			if err != nil {
				fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have connect err:%+v. connect count:%d,%d", time.Now(), num, wifiId, err, errConnectCount, connectCount))
				return
			}
		} else {
			connTcp, err = net.DialTimeout("tcp", serverAddr, 1*time.Second)
			if err != nil {
				fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have connect err:%+v. connect count:%d,%d", time.Now(), num, wifiId, err, errConnectCount, connectCount))
				return
			}
			connTcp.(*net.TCPConn).SetNoDelay(true)
		}
		connectCount++

		var bw *bufio.Writer
		var br *bufio.Reader
		if connTls != nil {
			bw = bufio.NewWriter(connTls)
			br = bufio.NewReader(connTls)
		} else {
			bw = bufio.NewWriter(connTcp)
			br = bufio.NewReader(connTcp)
		}

		token, err := send101Message(bw, br, wifiId, encryptKey)
		if token == "" {
			fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have 101 err:%+v", time.Now(), num, wifiId, err))
			return
		}
		err = send102Message(token, encryptKey, sendChan)
		if err != nil {
			fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have 102 err:%+v", time.Now(), num, wifiId, err))
		}
		err = send103Message(token, encryptKey, sendChan)
		if err != nil {
			fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have 103 err:%+v", time.Now(), num, wifiId, err))
		}
		var isClose bool = false
		var waitG1 *sync.WaitGroup = &sync.WaitGroup{}
		waitG1.Add(1)
		go func() {
			for {
				if isClose {
					break
				}
				err = heartbeat(sendChan)
				if err != nil {
					fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have 902 err:%+v", time.Now(), num, wifiId, err))
				}
				if isClose {
					break
				}
				time.Sleep(time.Duration(heartbeatSleep) * time.Second)
			}
			waitG1.Done()
		}()

		waitG1.Add(2)
		go func() {
			for i := 0; i < count; i++ {
				if isClose {
					break
				}
				err := send901Message(token, encryptKey, sendChan)
				if err != nil {
					fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have 901 err:%+v", time.Now(), num, wifiId, err))
				}
				if isClose {
					break
				}
				sleepTime := rand.Intn(maxSleep) + 1
				time.Sleep(time.Duration(sleepTime) * time.Second)
			}
			fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s finish 901 send", time.Now(), num, wifiId))
			isClose = true
			waitG1.Done()
		}()
		go func() {
			for {
				var mesage string
				mesage = <-sendChan
				bw.WriteString(mesage)
				bw.Flush()
				if strings.Contains(mesage, "\"id\":902") || strings.Contains(mesage, "\"id\":102") || strings.Contains(mesage, "\"id\":103") {
					if connTls != nil {
						connTls.SetReadDeadline(time.Now().Add(5 * time.Second))
					} else {
						connTcp.SetReadDeadline(time.Now().Add(5 * time.Second))
					}
					_, err := br.ReadString('\n')
					if err != nil {
						fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have send err:%+v, send message:%+v", time.Now(), num, wifiId, err, mesage))
						isClose = true
						errConnectCount++
					}
				}
			}
		}()
		waitG1.Wait()
		if connTls != nil {
			connTls.Close()
		} else {
			connTcp.Close()
		}
	}
}

func send101Message(bw *bufio.Writer, br *bufio.Reader, wifiId, encryptKey string) (string, error) {
	param := strings.Replace(paramString_101, "wifiid", wifiId, -1)
	md5Sign, err := sha256.Sha256Encode(encryptKey + param)
	if err != nil {
		return "", err
	}
	param101 := "HS" + param + md5Sign + "\r\n"

	bw.WriteString(param101)
	bw.Flush()

	response, err := br.ReadString('\n')
	if err != nil {
		return "", err
	}
	var token string
	if strings.Contains(response, "token") {
		resArray := strings.Split(response, ",")
		for _, element := range resArray {
			if strings.Contains(element, "token") {
				token = strings.Replace(strings.Replace(element, "\"token\":\"", "", -1), "\"", "", -1)
				if strings.Contains(token, "}") {
					arr := strings.Split(token, "}")
					token = arr[0]
				}
				break
			}
		}
		return token, nil
	} else {
		return "", errors.New("101 response is empty")
	}
}

func send901Message(token, encryptKey string, sendChan chan string) error {
	param := strings.Replace(paramString_901, "tokenv", token, -1)
	md5Sign, err := sha256.Sha256Encode(encryptKey + param)
	if err != nil {
		return err
	}
	param901 := "HS" + param + md5Sign + "\r\n"
	sendChan <- param901
	return nil
}

func send102Message(token, encryptKey string, sendChan chan string) error {
	param := strings.Replace(paramString_102, "tokenv", token, -1)
	md5Sign, err := sha256.Sha256Encode(encryptKey + param)
	if err != nil {
		return err
	}
	param102 := "HS" + param + md5Sign + "\r\n"
	sendChan <- param102
	return nil
}

func send103Message(token, encryptKey string, sendChan chan string) error {
	param := strings.Replace(paramString_103, "tokenv", token, -1)
	md5Sign, err := sha256.Sha256Encode(encryptKey + param)
	if err != nil {
		return err
	}
	param103 := "HS" + param + md5Sign + "\r\n"
	sendChan <- param103
	return nil
}

func heartbeat(sendChan chan string) error {
	sendChan <- "HS{\"id\":902}\r\n"
	return nil
}
